package com.chouren.goandroid.http.client;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpUriRequest;

import com.chouren.goandroid.http.comm.Network;
import com.chouren.goandroid.http.comm.Network.NetworkType;
import com.chouren.goandroid.http.exception.HttpNetException;
import com.chouren.goandroid.http.exception.HttpNetException.NetException;
import com.chouren.goandroid.http.parser.DataParser;
import com.chouren.goandroid.http.request.Request;
import com.chouren.goandroid.http.request.param.HttpParam;
import com.chouren.goandroid.http.response.InternalResponse;
import com.chouren.goandroid.http.response.Response;

import android.content.Context;
import android.util.Log;

public abstract class HttpClient {
	public static final int FLAG_NET_DISABLE_ALL = Network.NetworkType.None.value;
	public static final int FLAG_NET_DISABLE_MOBILE = Network.NetworkType.Mobile.value;
	public static final int FLAG_NET_DISABLE_WIFI = Network.NetworkType.Wifi.value;
	public static final int FLAG_NET_DISABLE_OTHER = Network.NetworkType.Other.value;
	
	/** 不使用某些网络  */
	protected int disableNetworkFlags;
	/** 是否统计时间和流量,默认开启   */
	public boolean doStatistics = true;
	/** 是否显示调试信息,默认开启   */
	protected boolean useDebug = true;
	/** 连接前是否判断网络状态   */
	protected boolean detectNetwork = true;
	/** 非幂等请求是否强制重试   */
	protected boolean forceRetry = false;
	/** 错误描述是否使用中文   */
	public boolean errorInChinese = true;
	
	protected StatisticsInfo statisticsInfo;
	protected static Context appContext;
	
	public abstract Response execute(Request request);
	
	public abstract HttpResponse execute(HttpUriRequest req) throws ClientProtocolException, IOException;

	public abstract <T> T execute(String uri, DataParser<T> parser, HttpMethod method);
	
	public abstract String get(String uri);

	public abstract <T> T get(String uri, DataParser<T> parser);

	public abstract <T> T get(String uri, HttpParam model, Class<T> clazz);
	
	public abstract String post(String uri);

	public abstract <T> T post(String uri, DataParser<T> parser);

	public abstract <T> T post(String uri, HttpParam model, Class<T> clazz);
	
	public StatisticsInfo getStatisticsInfo() {
		return statisticsInfo;
	}

	public Context getAppContext() {
		return appContext;
	}
	
	/**
	 * 配置{@link LiteHttpClient}
	 * @param context application 或者 activity context
	 * @param detectNetwork 连接前是否判断网络状态
	 * @param doStatistics 是否统计时间和流量
	 * @param forceRetry 非幂等请求是否强制重试
	 * @param errorInChinese 出现异常是否使用中文描述，建议国内开发者设置ture
	 */
	public void config(Context context, boolean detectNetwork, boolean doStatistics, boolean forceRetry,
			boolean errorInChinese, boolean useDebug) {
		if (context != null) appContext = context.getApplicationContext();
		this.detectNetwork = detectNetwork;
		this.doStatistics = doStatistics;
		this.forceRetry = forceRetry;
		this.errorInChinese = errorInChinese;
		this.useDebug = useDebug;
	}
	
	/**
	 * 网络请求的统计信息
	 */
	public static class StatisticsInfo {
		private AtomicLong connectTime = new AtomicLong();
		private AtomicLong dataLength = new AtomicLong();

		public void addConnectTime(long time) {
			connectTime.addAndGet(time);
		}

		public void addDataLength(long len) {
			dataLength.addAndGet(len);
		}

		public long getConnectTime() {
			return connectTime.longValue();
		}

		public long getDataLength() {
			return dataLength.longValue();
		}
	}
	
	/**
	 * 获取response
	 */
	public InternalResponse getInternalResponse() {
		final InternalResponse innerResponse = new InternalResponse();
		
		if(detectNetwork | doStatistics) innerResponse.setExecuteListener(new HttpClient.ExecuteListener() {
			long start, connect, read;
			
			@Override
			public void onStart() throws HttpNetException {
				NetworkType type = null;
				if(detectNetwork) {
					type = Network.getConnectedType(appContext);
					if(NetworkType.None == type) throw new HttpNetException(NetException.NetworkError);
				}
				if((disableNetworkFlags & FLAG_NET_DISABLE_ALL) == FLAG_NET_DISABLE_ALL) {
					throw new HttpNetException(NetException.NetworkDisabled);
				}else if(disableNetworkFlags > FLAG_NET_DISABLE_ALL) {
					if (type == null) type = Network.getConnectedType(appContext);
					if ((type.value & disableNetworkFlags) == type.value) throw new HttpNetException(
							NetException.NetworkDisabled);
				}
				if (doStatistics) {
					if (statisticsInfo == null) statisticsInfo = new StatisticsInfo();
					start = System.currentTimeMillis();
				}
			}
			
			@Override
			public void onPreRead() {
				if (doStatistics) connect = System.currentTimeMillis();
			}
			
			@Override
			public void onPreConnect() {
				if (doStatistics) Log.d("HttpClient", "http read data time: " + (System.currentTimeMillis() - read));
			}
			
			@Override
			public void onEnd() {
				if (doStatistics) {
					long time = start > 0 ? System.currentTimeMillis() - start : 0;
					innerResponse.setConnectTime(time);
					statisticsInfo.addConnectTime(time);
					Log.d("HttpClient", "http total time: " + time + ", global total time: "
							+ statisticsInfo.getConnectTime());

					long headLen = innerResponse.getContentLength();
					long readLen = innerResponse.getReadedLength();
					long len = 0;
					if (readLen > 0) {
						len = headLen > 0 ? headLen : readLen;
					}
					statisticsInfo.addDataLength(len);
					Log.d("HttpClient", "http len in header: " + headLen + ", readed len: " + readLen
							+ ", global total len: " + statisticsInfo.getDataLength());
				}
			}
			
			@Override
			public void onAfterRead() {
				if (doStatistics) read = System.currentTimeMillis();
			}
			
			@Override
			public void onAfterConnect() {
				if (doStatistics) Log.d("HttpClient", "http connect use time: "
						+ (System.currentTimeMillis() - connect));
			}
		});
		
		return innerResponse;
	} 
	
	/**
	 * http请求执行的监听
	 */
	public static interface ExecuteListener {
		public void onStart() throws HttpNetException;

		public void onPreConnect();

		public void onAfterConnect();

		public void onPreRead();

		public void onAfterRead();

		public void onEnd();
	}
	
	/**
	 * 是否显示调试信息
	 */
	public void setUseDebug(boolean useDebug) {
		this.useDebug = useDebug;
	}
	
}
